1. Introdução (Business Understanding)¶

Este notebook apresenta a Análise Exploratória do Programa de Subvenção ao Prêmio do Seguro Rural (PSR) no Brasil (2006–2025), seguindo a metodologia CRISP-DM. O objetivo é evidenciar:

  • Evolução temporal do programa (apólices, prêmio, valor segurado, subvenção);
  • Distribuição geográfica por UF/município;
  • Padrões por cultura e seguradora;
  • Eventos preponderantes causadores de sinistros;
  • Retenção de segurados;
  • Localidades em crescimento/queda.

Público-alvo: recrutadores e profissionais do agronegócio e seguros rurais.

2. Entendimento dos Dados (Data Understanding)¶

Nesta seção resumimos o dataset consolidado (nível processed) e referências de qualidade provenientes do relatório de colunas gerado na etapa anterior.

In [1]:
# === Setup e carregamento ===
import pandas as pd
import numpy as np
from pathlib import Path
import matplotlib.pyplot as plt

# Caminhos (use relativo ao repositório)
DATA_DIR = Path(r"C:\Users\fred\Documents\Estudo de dados\Projeto\Seguro Rural\data")
PROC = DATA_DIR / 'processed'
INTERIM = DATA_DIR / 'interim'
DOCS = Path('../docs')

# Dataset final esperado (ajuste se necessário)
PARQUET_FINAL = PROC / 'psr_2006_2025.parquet'

# Relatório de colunas (opcional, vindo da etapa anterior)
RELATORIO_COLUNAS = DOCS / 'relatorio_colunas.csv'  # mova seu arquivo para docs/

# Carregar dataset final
df = pd.read_parquet(PARQUET_FINAL)
print('Registros:', len(df), '| Colunas:', len(df.columns))
df.head(3)
Registros: 1712384 | Colunas: 68
Out[1]:
NM_RAZAO_SOCIAL CD_PROCESSO_SUSEP NR_PROPOSTA ID_PROPOSTA DT_PROPOSTA DT_INICIO_VIGENCIA DT_FIM_VIGENCIA NM_SEGURADO NR_DOCUMENTO_SEGURADO NM_MUNICIPIO_PROPRIEDADE ... METR_AREA_MEDIA METR_SUBVENCAO_RELATIVA MES_APOLICE NR_DOCUMENTO_SEGURADO_NORM CHAVE_SEGURADO PRESENCA_SEGURADO_ANO FLAG_PREMIO_NULO FLAG_PREMIO_NEGATIVO FLAG_GARANTIA_NULA FLAG_GARANTIA_INVALIDA
0 aliança_do_brasil_seguros_s/a. 15414901479201927 50405357 1045544 2019-08-29 2019-08-29 2020-08-28 jose_procopio_bezerra_neto ***53043172 rio_verde ... 0.001726 NaN 9 53043172 53043172 True True False True False
1 aliança_do_brasil_seguros_s/a. 15414901479201927 50405359 1038777 2019-08-29 2019-08-29 2020-08-28 joao_augusto_dedemo_prado ***47332869 veríssimo ... 0.001273 NaN 9 47332869 47332869 True True False True False
2 aliança_do_brasil_seguros_s/a. 15414901479201927 50405361 1038713 2019-08-29 2019-08-29 2020-08-28 joao_augusto_dedemo_prado ***47332869 veríssimo ... 0.001500 NaN 9 47332869 47332869 True True False True False

3 rows × 68 columns

In [2]:
# === Resumo descritivo e qualidade ===
summary = {
    'periodo_min': pd.to_datetime(df['DT_APOLICE'], errors='coerce').min(),
    'periodo_max': pd.to_datetime(df['DT_APOLICE'], errors='coerce').max(),
    'n_seguradoras': df['NM_RAZAO_SOCIAL'].nunique() if 'NM_RAZAO_SOCIAL' in df.columns else np.nan,
    'n_culturas': df['NM_CULTURA_GLOBAL'].nunique() if 'NM_CULTURA_GLOBAL' in df.columns else np.nan,
    'n_municipios': df['NM_MUNICIPIO_PROPRIEDADE'].nunique() if 'NM_MUNICIPIO_PROPRIEDADE' in df.columns else np.nan,
    'n_ufs': df['SG_UF_PROPRIEDADE'].nunique() if 'SG_UF_PROPRIEDADE' in df.columns else np.nan,
}
pd.Series(summary)
Out[2]:
periodo_min      2006-09-14 00:00:00
periodo_max      2025-08-21 00:00:00
n_seguradoras                     20
n_culturas                        69
n_municipios                    5067
n_ufs                             27
dtype: object
In [3]:
# % de nulos em colunas-chave
cols_chave = [
    'VL_PREMIO_LIQUIDO','VL_LIMITE_GARANTIA','VALOR_INDENIZAÇÃO',
    'VL_SUBVENCAO_FEDERAL','NR_AREA_TOTAL','NM_CULTURA_GLOBAL','SG_UF_PROPRIEDADE'
]
nulos = (
    df[cols_chave].isna().mean().sort_values(ascending=False)*100
).round(2).rename('%_nulos')
display(nulos.to_frame())

# Gráfico simples de % nulos (matplotlib sem cores específicas)
plt.figure(figsize=(8,4))
nulos.plot(kind='bar')
plt.title('% de nulos - colunas-chave')
plt.ylabel('%')
plt.xlabel('coluna')
plt.show()
%_nulos
VL_SUBVENCAO_FEDERAL 96.02
VALOR_INDENIZAÇÃO 92.16
VL_PREMIO_LIQUIDO 91.16
VL_LIMITE_GARANTIA 65.02
NR_AREA_TOTAL 37.38
NM_CULTURA_GLOBAL 0.00
SG_UF_PROPRIEDADE 0.00
No description has been provided for this image

Resumo do Entendimento dos Dados (Data Understanding)¶

  • Dataset consolidado com 1,7 milhão de registros e 68 variáveis.
  • Cobertura temporal: 2006–2025.
  • Abrange 20 seguradoras, 69 culturas agrícolas, 5.067 municípios em 27 UFs.

Completude das variáveis-chave:¶

  • VL_SUBVENCAO_FEDERAL: ~96% nulos
  • VALOR_INDENIZAÇÃO: ~92% nulos
  • VL_PREMIO_LIQUIDO: ~91% nulos
  • VL_LIMITE_GARANTIA: ~65% nulos
  • NR_AREA_TOTAL: ~37% nulos
  • Variáveis categóricas (NM_CULTURA_GLOBAL, SG_UF_PROPRIEDADE) → sem nulos

📌 Interpretação: grande parte dos nulos decorre da própria natureza do PSR (nem todas apólices têm subvenção ou indenização).


3. Preparação (Data Preparation) — resumo do pipeline¶

  • Camadas: raw → interim → processed
  • Tratamentos realizados: padronização de tipos, normalização textual, conversão de datas, remoção de duplicatas por ID_PROPOSTA
  • Flags: FLAG_PREMIO_ZERO, FLAG_RELACAO_PREMIO_SEGURADO, FLAG_SUBVENCAO_EXCESSO, FLAG_PRODUT_*_OUTLIER, FLAG_*DATAS*, FLAG_PREMIO_NULO/GARANTIA_*
  • Métricas: METR_TAXA_PREMIO, METR_SINISTRALIDADE, METR_AREA_MEDIA, METR_SUBVENCAO_RELATIVA, ANO_APOLICE, MES_APOLICE
  • Identificação: CHAVE_SEGURADO (documento normalizado) para análises de retenção.

4. Análises Exploratórias (Evaluation)¶

4.1 Evolução temporal¶

Perguntas:

  • Como evoluíram apólices, valor segurado, prêmio e subvenção ao longo do tempo? Notas de qualidade:
  • Calcular séries com e sem registros marcados por flags (ex.: FLAG_PREMIO_NULO).
In [4]:
# Série histórica: contagem de apólices por ano
serie_apo = df.groupby('ANO_APOLICE', dropna=False)['ID_PROPOSTA'].nunique().sort_index()
display(serie_apo.to_frame('n_apolices'))

plt.figure()
serie_apo.plot()
plt.title('Nº de apólices por ano')
plt.xlabel('Ano')
plt.ylabel('Nº apólices')
plt.show()
n_apolices
ANO_APOLICE
2006 21743
2007 31168
2008 59802
2009 69039
2010 53145
2011 60556
2012 62158
2013 102583
2014 117597
2015 39892
2016 74331
2017 66352
2018 63062
2019 92683
2020 184253
2021 209199
2022 121255
2023 102811
2024 134618
2025 46137
No description has been provided for this image
In [13]:
# === Série histórica: valores financeiros (em milhões de R$) ===
by_year = df.groupby('ANO_APOLICE', dropna=False).agg(
    valor_segurado=('VL_LIMITE_GARANTIA','sum'),
    premio=('VL_PREMIO_LIQUIDO','sum'),
    sub_federal=('VL_SUBVENCAO_FEDERAL','sum'),
    indenizacao=('VALOR_INDENIZAÇÃO','sum')
).sort_index()

# Converter para milhões
by_year_mi = (by_year / 1e6).round(2)

display(by_year_mi.head())

# Plotar em milhões
plt.figure(figsize=(10,6))
by_year_mi[['valor_segurado','premio','sub_federal','indenizacao']].fillna(0).plot(ax=plt.gca())
plt.title('Séries financeiras por ano (em milhões de R$)', fontsize=12)
plt.xlabel('Ano')
plt.ylabel('R$ milhões')
plt.grid(True, linestyle="--", alpha=0.5)
plt.tight_layout()
plt.show()
valor_segurado premio sub_federal indenizacao
ANO_APOLICE
2006 1879.11 3.34 1.59 1.02
2007 947.55 9.31 4.34 4.89
2008 2326.93 13.59 7.31 11.82
2009 3558.42 26.71 11.52 12.40
2010 2694.04 17.78 7.78 11.26
No description has been provided for this image

Evolução temporal¶

  • Nº de apólices cresceu até 2021, com pico de 209 mil apólices, e queda em anos recentes.
  • Valores financeiros em tendência de expansão, com forte aceleração a partir de 2019.

4.2 Distribuição geográfica¶

  • Ranking por UF de apólices, prêmio, valor segurado e sinistralidade média.
  • (Opcional) Mapa coroplético por UF (requer geopandas e um shapefile/geojson).
In [14]:
# === Rankings por UF (em milhões de R$) ===
uf_agg = df.groupby('SG_UF_PROPRIEDADE', dropna=False).agg(
    n_apolices=('ID_PROPOSTA','nunique'),
    valor_segurado=('VL_LIMITE_GARANTIA','sum'),
    premio=('VL_PREMIO_LIQUIDO','sum'),
    sinistralidade=('METR_SINISTRALIDADE','mean')
).sort_values('n_apolices', ascending=False)

# Converter valores para milhões
uf_agg_fmt = uf_agg.copy()
uf_agg_fmt['valor_segurado'] = (uf_agg_fmt['valor_segurado'] / 1e6).round(2)
uf_agg_fmt['premio'] = (uf_agg_fmt['premio'] / 1e6).round(2)

# Renomear colunas para clareza
uf_agg_fmt.rename(columns={
    'valor_segurado': 'valor_segurado (R$ mi)',
    'premio': 'premio (R$ mi)',
    'sinistralidade': 'sinistralidade (média)'
}, inplace=True)

display(uf_agg_fmt.head(15))
n_apolices valor_segurado (R$ mi) premio (R$ mi) sinistralidade (média)
SG_UF_PROPRIEDADE
pr 640980 20636.37 340.58 1.036168
rs 358099 37056.38 515.20 2.155295
sp 236341 19371.34 523.30 2.428079
sc 126625 12770.06 259.35 1.552355
mg 107729 11295.26 141.84 3.547424
go 86398 5790.13 64.12 0.738290
ms 78779 6338.41 105.14 0.738306
mt 29581 4513.75 43.36 3.678588
es 12674 437.91 1.60 0.000000
ba 11569 1443.57 15.88 0.220777
to 6184 1011.36 9.59 2.826513
ma 4579 583.60 5.43 1.384769
pi 2294 371.70 4.66 8.624914
df 2268 2059.79 5.03 1.794191
se 1972 118.12 0.99 0.000000
In [21]:
import plotly.express as px
import requests

# === GeoJSON simplificado das UFs ===
url_uf = "https://raw.githubusercontent.com/codeforamerica/click_that_hood/master/public/data/brazil-states.geojson"
geojson_uf = requests.get(url_uf).json()

# Dicionário sigla -> nome (sem acentos, como no GeoJSON)
uf_names = {
    'AC':'Acre','AL':'Alagoas','AP':'Amapa','AM':'Amazonas','BA':'Bahia','CE':'Ceara',
    'DF':'Distrito Federal','ES':'Espirito Santo','GO':'Goias','MA':'Maranhao','MT':'Mato Grosso',
    'MS':'Mato Grosso do Sul','MG':'Minas Gerais','PA':'Para','PB':'Paraiba','PR':'Parana',
    'PE':'Pernambuco','PI':'Piaui','RJ':'Rio de Janeiro','RN':'Rio Grande do Norte',
    'RS':'Rio Grande do Sul','RO':'Rondonia','RR':'Roraima','SC':'Santa Catarina',
    'SP':'Sao Paulo','SE':'Sergipe','TO':'Tocantins'
}

# Preparar DataFrame para o mapa
df_map = uf_agg.reset_index().copy()
df_map["SG_UF_PROPRIEDADE"] = df_map["SG_UF_PROPRIEDADE"].str.upper()
df_map["UF_NAME"] = df_map["SG_UF_PROPRIEDADE"].map(uf_names)

# Criar métricas em milhões/bilhões
df_map["premio_mi"] = (df_map["premio"] / 1e6).round(2)
df_map["valor_segurado_bi"] = (df_map["valor_segurado"] / 1e9).round(2)
df_map["sinistralidade"] = df_map["sinistralidade"].round(2)

# Centralização e projeção para o Brasil
geo_config = dict(
    fitbounds="locations",
    visible=False,
    projection_type="mercator",
    center={"lat": -14, "lon": -52}
)

# === 1. Prêmio ===
fig1 = px.choropleth(
    df_map,
    geojson=geojson_uf,
    featureidkey="properties.name",
    locations="UF_NAME",
    color="premio_mi",
    color_continuous_scale="Blues",
    title="Prêmio de Seguro Rural por UF (2006–2025, R$ milhões)",
    hover_data={"premio_mi": True, "n_apolices": True, "sinistralidade": True}
)
fig1.update_geos(**geo_config)
fig1.show()

# === 2. Valor Segurado ===
fig2 = px.choropleth(
    df_map,
    geojson=geojson_uf,
    featureidkey="properties.name",
    locations="UF_NAME",
    color="valor_segurado_bi",
    color_continuous_scale="Greens",
    title="Valor Segurado por UF (2006–2025, R$ bilhões)",
    hover_data={"valor_segurado_bi": True, "n_apolices": True, "sinistralidade": True}
)
fig2.update_geos(**geo_config)
fig2.show()

# === 3. Sinistralidade ===
fig3 = px.choropleth(
    df_map,
    geojson=geojson_uf,
    featureidkey="properties.name",
    locations="UF_NAME",
    color="sinistralidade",
    color_continuous_scale="Reds",
    title="Sinistralidade Média por UF (2006–2025)",
    hover_data={"sinistralidade": True, "n_apolices": True, "premio_mi": True}
)
fig3.update_geos(**geo_config)
fig3.show()

Distribuição geográfica¶

  • PR, RS, SP e SC concentram a maior parte das apólices e prêmios.
  • Mapas coropléticos mostraram contraste:
    • Sul/Sudeste com maior volume,
    • Norte/Nordeste com menor penetração do PSR.
  • Sinistralidade média mais elevada em MG, MT, PI.

4.3 Análise por cultura¶

  • Ranking de culturas por apólices, prêmio e valor segurado.
  • Sinistralidade média por cultura.
In [22]:
# === Ranking por cultura (em milhões de R$) ===
cult = df.groupby('NM_CULTURA_GLOBAL', dropna=False).agg(
    n_apolices=('ID_PROPOSTA','nunique'),
    valor_segurado=('VL_LIMITE_GARANTIA','sum'),
    premio=('VL_PREMIO_LIQUIDO','sum'),
    sinistralidade=('METR_SINISTRALIDADE','mean')
).sort_values('n_apolices', ascending=False)

# Converter para milhões
cult_fmt = cult.copy()
cult_fmt['valor_segurado'] = (cult_fmt['valor_segurado'] / 1e6).round(2)
cult_fmt['premio'] = (cult_fmt['premio'] / 1e6).round(2)

# Renomear colunas
cult_fmt.rename(columns={
    'valor_segurado': 'valor_segurado (R$ mi)',
    'premio': 'premio (R$ mi)',
    'sinistralidade': 'sinistralidade (média)'
}, inplace=True)

display(cult_fmt.head(20))

# === Gráfico de barras (Top 10 culturas por nº de apólices) ===
top10 = cult_fmt.head(10)['n_apolices']
plt.figure(figsize=(8,5))
top10.plot(kind='barh')
plt.title('Top 10 culturas por nº de apólices')
plt.xlabel('nº apólices')
plt.ylabel('cultura')
plt.gca().invert_yaxis()
plt.grid(axis="x", linestyle="--", alpha=0.5)
plt.show()
n_apolices valor_segurado (R$ mi) premio (R$ mi) sinistralidade (média)
NM_CULTURA_GLOBAL
soja 728850 39182.67 488.90 1.067488
milho_2ª_safra 246653 8857.74 194.18 0.521013
uva 148604 9094.96 192.69 2.860795
trigo 143830 11037.01 213.30 1.506704
milho_1ª_safra 91627 4600.17 40.56 0.977428
café 72083 5057.34 25.65 4.325146
arroz 59743 9292.20 57.25 1.852670
maçã 35317 8193.51 161.13 1.467477
cana-de-açúcar 33265 2988.56 6.35 10.742189
tomate 26614 4526.41 290.81 3.082442
pecuário 24826 5120.28 20.59 3.636350
cebola 21078 2000.71 99.89 1.441182
feijão_1ª_safra 13382 2646.46 9.34 2.122794
pêssego 11568 705.43 31.43 2.388052
ameixa 8050 427.37 41.53 2.424768
caqui 6409 496.77 41.13 2.253781
sorgo 6127 293.02 5.34 0.385538
floresta 4759 4840.01 6.94 18.516117
cevada 4640 725.25 6.73 2.034733
alho 2765 635.53 27.21 1.594235
No description has been provided for this image

Análise por cultura¶

  • Soja domina o PSR em nº de apólices e valor segurado.
  • Outras culturas relevantes: milho (1ª e 2ª safra), trigo, uva, café, arroz.
  • Culturas com sinistralidade elevada: floresta, cana-de-açúcar, tomate.

4.4 Análise por seguradora¶

  • Ranking de seguradoras por nº de apólices e prêmio.
  • Evolução de participação ao longo do tempo (se houver interesse).
In [9]:
seg = df.groupby('NM_RAZAO_SOCIAL', dropna=False).agg(
    n_apolices=('ID_PROPOSTA','nunique'),
    premio=('VL_PREMIO_LIQUIDO','sum'),
    sinistralidade=('METR_SINISTRALIDADE','mean')
).sort_values('n_apolices', ascending=False)

display(seg.head(15))

# Barras das top 10 por nº apólices
plt.figure()
seg.head(10)['n_apolices'].plot(kind='barh')
plt.title('Top 10 seguradoras por nº de apólices')
plt.xlabel('nº apólices')
plt.ylabel('seguradora')
plt.gca().invert_yaxis()
plt.show()
n_apolices premio sinistralidade
NM_RAZAO_SOCIAL
brasilseg_companhia_de_seguros 595443 235306692.0 0.940515
essor_seguros_s.a. 232637 465665151.0 1.626880
mapfre_seguros_gerais_s.a. 230985 36344628.0 1.368887
swiss_re_corporate_solutions_brasil_s.a. 131220 70026310.0 4.361593
allianz_seguros_s.a 102612 31420958.0 1.916051
nobre_seguradora_do_brasil_s.a 82500 90679014.0 3.397220
sancor_seguros_do_brasil_s.a. 79168 74702697.0 1.536240
fairfax_brasil_seguros_corporativos_s/a 54999 45250088.0 3.198631
newe_seguros_s.a 50265 52769113.0 0.537558
tokio_marine_seguradora_s.a. 38657 353875952.0 1.416964
porto_seguro_companhia_de_seguros_gerais 35968 494307909.0 3.073472
sompo_seguros_s/a 20286 13953045.0 0.449780
aliança_do_brasil_seguros_s/a. 18474 46870262.0 1.175323
sombrero_seguros_s/a 18345 14748120.0 0.235262
too_seguros_s.a. 13709 7706937.0 1.711258
No description has been provided for this image

Análise por seguradora¶

  • Maior nº de apólices: Brasilseg.
  • Maior prêmio concentrado em seguradoras como Essor e Porto Seguro.
  • Há diferenças marcantes na sinistralidade média entre seguradoras.

4.5 Eventos de sinistro¶

  • Frequência e impacto financeiro dos EVENTO_PREPONDERANTE.
  • Ranking por UF/cultura.
In [23]:
# === Eventos preponderantes ===
evt = df.assign(evento=df['EVENTO_PREPONDERANTE'].fillna('não informado'))
evt_agg = evt.groupby('evento', dropna=False).agg(
    n_apolices=('ID_PROPOSTA','size'),
    indenizacao=('VALOR_INDENIZAÇÃO','sum')
).sort_values('indenizacao', ascending=False)

# Converter indenização para milhões
evt_agg['indenizacao (R$ mi)'] = (evt_agg['indenizacao'] / 1e6).round(2)

# Selecionar colunas finais com nomes claros
evt_agg_fmt = evt_agg[['n_apolices', 'indenizacao (R$ mi)']]

# Mostrar tabela (Top 15)
display(evt_agg_fmt.head(15).style.set_properties(**{'text-align': 'center'})
        .set_table_styles([dict(selector='th', props=[('text-align', 'center')])]))
  n_apolices indenizacao (R$ mi)
evento    
seca 180687 593.130000
granizo 75823 336.950000
geada 42746 182.940000
chuva_excessiva 19773 57.450000
incêndio 610 30.680000
morte 1554 27.530000
inundação/tromba_d´água 4069 18.110000
ventos_fortes/frios 5518 11.130000
demais_causas 44089 6.010000
variação_excessiva_de_temperatura 1273 2.860000
queda_de_parreiral 297 0.970000
doenças_e_pragas 34 0.190000
raio 61 0.140000
perda_de_qualidade 31 0.140000
- 1335760 0.010000

Eventos de sinistro¶

  • Seca, granizo e geada respondem pela maior parte das indenizações.
  • Eventos de baixa frequência, mas alto impacto local, incluem chuva excessiva e incêndio.

4.6 Retenção de segurados¶

  • % de segurados que permanecem de um ano para o outro.
  • Quebrar por UF/cultura (opcional).
In [24]:
presenca = (
    df.dropna(subset=['CHAVE_SEGURADO','ANO_APOLICE'])
      .groupby(['ANO_APOLICE','CHAVE_SEGURADO']).size().reset_index(name='n')
)

anos = sorted(presenca['ANO_APOLICE'].unique())
rows=[]
for a in anos:
    base_a = set(presenca.loc[presenca['ANO_APOLICE']==a, 'CHAVE_SEGURADO'])
    base_b = set(presenca.loc[presenca['ANO_APOLICE']==a+1, 'CHAVE_SEGURADO'])
    if len(base_a)==0: 
        continue
    ret = len(base_a & base_b)/len(base_a)
    rows.append({'ano': int(a), 'retencao_t_to_t1': ret})

ret_df = pd.DataFrame(rows).sort_values('ano')
display(ret_df)

# Plot corrigido
plt.figure(figsize=(10,5))
plt.plot(ret_df['ano'].to_numpy(), ret_df['retencao_t_to_t1'].to_numpy(), marker='o')
plt.title('Retenção de segurados (t → t+1)')
plt.xlabel('Ano')
plt.ylabel('Retenção (%)')
plt.ylim(0,1)  # eixo fixo de 0 a 1
plt.grid(True, linestyle="--", alpha=0.6)
plt.show()
ano retencao_t_to_t1
0 2006 0.584691
1 2007 0.712186
2 2008 0.678336
3 2009 0.567272
4 2010 0.664593
5 2011 0.672739
6 2012 0.790807
7 2013 0.729815
8 2014 0.294511
9 2015 0.645214
10 2016 0.599294
11 2017 0.586453
12 2018 0.686626
13 2019 0.784253
14 2020 0.733183
15 2021 0.495902
16 2022 0.556983
17 2023 0.636441
18 2024 0.295393
19 2025 0.000000
No description has been provided for this image

Retenção de segurados¶

  • Retenção média variou entre 50–80%, com quedas bruscas em 2014, 2021 e 2024.
  • Picos de fidelização ocorreram em 2012 e 2019 (>75%).

4.7 Crescimento e queda (YoY)¶

  • Identificar localidades (UF/município) que mais cresceram/caíram em nº de apólices e prêmio.
In [25]:
# === Cálculo de YoY com limpeza ===

loc_year = (
    df.groupby(['ANO_APOLICE','SG_UF_PROPRIEDADE','NM_MUNICIPIO_PROPRIEDADE'], dropna=False)
      .agg(n_apolices=('ID_PROPOSTA','nunique'),
           premio=('VL_PREMIO_LIQUIDO','sum'))
      .reset_index()
      .sort_values(['SG_UF_PROPRIEDADE','NM_MUNICIPIO_PROPRIEDADE','ANO_APOLICE'])
)

# Variações ano a ano
loc_year['variação_apolices_yoy (%)'] = loc_year.groupby(
    ['SG_UF_PROPRIEDADE','NM_MUNICIPIO_PROPRIEDADE']
)['n_apolices'].pct_change() * 100

loc_year['variação_premio_yoy (%)'] = loc_year.groupby(
    ['SG_UF_PROPRIEDADE','NM_MUNICIPIO_PROPRIEDADE']
)['premio'].pct_change() * 100

# Filtrar municípios com pelo menos 20 apólices no ano anterior
loc_year['n_apolices_ano_anterior'] = loc_year.groupby(
    ['SG_UF_PROPRIEDADE','NM_MUNICIPIO_PROPRIEDADE']
)['n_apolices'].shift(1)

loc_year_filtrado = loc_year[loc_year['n_apolices_ano_anterior'] >= 20].copy()

# Remover infinitos e valores extremos
loc_year_filtrado.replace([np.inf, -np.inf], np.nan, inplace=True)

# Último ano disponível
ultimo = loc_year_filtrado['ANO_APOLICE'].max()

crescimentos = (
    loc_year_filtrado[loc_year_filtrado['ANO_APOLICE']==ultimo]
    .sort_values('variação_apolices_yoy (%)', ascending=False)
    .head(15)
)

quedas = (
    loc_year_filtrado[loc_year_filtrado['ANO_APOLICE']==ultimo]
    .sort_values('variação_apolices_yoy (%)', ascending=True)
    .head(15)
)

# Mostrar resultados
display(crescimentos[['SG_UF_PROPRIEDADE','NM_MUNICIPIO_PROPRIEDADE','n_apolices',
                      'variação_apolices_yoy (%)','variação_premio_yoy (%)']])

display(quedas[['SG_UF_PROPRIEDADE','NM_MUNICIPIO_PROPRIEDADE','n_apolices',
                'variação_apolices_yoy (%)','variação_premio_yoy (%)']])
SG_UF_PROPRIEDADE NM_MUNICIPIO_PROPRIEDADE n_apolices variação_apolices_yoy (%) variação_premio_yoy (%)
47540 rs coronel_barros 44 41.935484 88.536396
46163 es vila_pavão 44 18.918919 NaN
47053 pr califórnia 40 11.111111 NaN
48217 sp ribeirão_grande 26 4.000000 36.647042
47067 pr carambeí 86 3.614458 -11.445013
47319 pr rio_branco_do_ivaí 40 0.000000 NaN
47401 pr uniflor 36 -2.702703 210.292958
47112 pr espigão_alto_do_iguaçu 40 -4.761905 -100.000000
47710 rs saldanha_marinho 37 -5.128205 -65.115011
46783 ms douradina 72 -8.860759 -66.666667
46739 mg unaí 64 -11.111111 NaN
47404 pr ventania 20 -13.043478 -100.000000
46632 mg passa_tempo 75 -13.793103 61.804710
47392 pr tibagi 147 -15.517241 -89.240710
48249 sp sumaré 39 -17.021277 -17.882348
SG_UF_PROPRIEDADE NM_MUNICIPIO_PROPRIEDADE n_apolices variação_apolices_yoy (%) variação_premio_yoy (%)
47922 sc turvo 1 -99.702381 -100.000000
47892 sc praia_grande 1 -99.099099 -100.000000
47275 pr paulo_frontin 1 -99.090909 -100.000000
47504 rs cacequi 1 -99.090909 -83.414967
47508 rs camaquã 1 -98.989899 -100.000000
47913 sc são_joão_do_sul 1 -98.969072 -98.111143
47761 rs são_pedro_do_sul 1 -98.823529 NaN
47908 sc são_domingos 1 -98.529412 -100.000000
46819 ms paraíso_das_águas 1 -98.387097 NaN
47478 rs arroio_grande 3 -98.203593 -100.000000
47089 pr coronel_domingos_soares 1 -98.181818 NaN
46328 go uruaçu 1 -98.076923 NaN
46052 ba formosa_do_rio_preto 1 -98.000000 -98.888177
47488 rs barros_cassal 1 -97.916667 NaN
46715 mg são_joão_del_rei 1 -97.916667 -100.000000

Crescimento e queda (YoY)¶

  • Municípios como Coronel Barros (RS) mostraram forte crescimento em 2025 (+42%).
  • Outros, como Turvo (SC), praticamente saíram do programa (queda de -99%).
  • Importante filtrar municípios com pelo menos 20 apólices no ano anterior para evitar distorções estatísticas.

5. Discussão e Limitações¶

Principais achados:¶

  • Forte concentração regional (Sul/Sudeste).
  • Cultura da soja é dominante, mas milho e trigo também são relevantes.
  • Seca é o principal evento de sinistro, refletindo vulnerabilidade climática.
  • Retenção irregular: há anos com forte fidelização e anos de queda abrupta.
  • Alguns municípios estão em clara expansão, enquanto outros praticamente abandonaram o PSR.

Limitações:¶

  • Elevada taxa de nulos em variáveis financeiras (prêmio, limite, subvenção, indenização).
  • Inconsistências em datas de vigência, mantidas como flags.
  • Dependência de dados administrativos, sujeitos a falhas de preenchimento por seguradora/ano.

Oportunidades:¶

  • Enriquecimento com dados meteorológicos/satelitais para explicar sinistros.
  • Criação de dashboards interativos (Plotly, Streamlit, PowerBI).
  • Modelagem preditiva para risco climático e retenção de segurados.

6. Conclusão¶

  • Foi construído um pipeline reprodutível (raw → interim → processed), documentado com dicionário de variáveis e testes de qualidade.
  • As análises exploratórias cobriram:
    • Negócio: dinâmica do PSR ao longo de 20 anos;
    • Geografia: distribuição por UF/município;
    • Produção: padrões por cultura;
    • Mercado: seguradoras;
    • Risco: eventos de sinistro;
    • Clientes: retenção e churn.
In [ ]: